package com.spzx.cart.service.impl;

import com.spzx.cart.api.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuPrice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.ReactiveHashOperationsExtensionsKt;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartServiceImpl implements ICartService {

    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    RemoteProductService remoteProductService;

    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        // 准备 Redis Hash类型 大key
        Long userId = SecurityContextHolder.getUserId();  // 从当前线程上获取共享数据。HeaderInterceptor拦截器获取请求头信息，将数据绑定到当前线程上。
        String cartKey = getCartKey(userId);

        // 准备 Redis Hash类型 小key
        String hashKey = String.valueOf(skuId);

        // 从 Redis Hash 类型中获取操作购物车的 k-v map
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);

        // 单个商品数量的最大限额
        int threshold = 99;

        if (boundHashOperations.hasKey(hashKey)){ //以前添加过这个商品， 修改购物车这个商品的数量
            CartInfo cartInfo = boundHashOperations.get(hashKey);
            int newSkuNum = cartInfo.getSkuNum() + skuNum;
            cartInfo.setSkuNum(newSkuNum > threshold ? threshold : newSkuNum);
            cartInfo.setUpdateTime(new Date());
            boundHashOperations.put(hashKey,cartInfo);
        } else { //首次添加
            // 购物车商品，每个人每次最多不超过 50 个商品
            Long size = boundHashOperations.size();
            if (++size > 50) {
                throw new ServiceException("超出购物车最大商品数量50 个！！");
            }

            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);

            // 根据skuId 调用 spzx-product 微服务接口，获取 productSku 数据
            R<ProductSku> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (productSkuResult.getCode() == R.FAIL) {
                throw new ServiceException(productSkuResult.getMsg());
            }

            ProductSku productSku = productSkuResult.getData();
            cartInfo.setCartPrice(productSku.getSalePrice());
            cartInfo.setSkuPrice(productSku.getSalePrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setThumbImg(productSku.getThumbImg());
            cartInfo.setSkuName(productSku.getSkuName());
            cartInfo.setIsChecked(1);
            cartInfo.setCreateTime(new Date());

            boundHashOperations.put(hashKey,cartInfo);

        }
    }

    // 生成购物车的 key
    private String getCartKey(Long userId){
        String cartKey = "user:cart:" + userId;
        return cartKey;
    }

    /**
     * 查看购物车列表数据
     * @return 列表数据
     */
    @Override
    public List<CartInfo> cartList() {
        // 准备 Redis Hash类型大key
        Long userId = SecurityContextHolder.getUserId(); //从当前线程上获取共享数据， HeaderInterceptor 拦截器获取请求头信息，将数据绑定到当前线程上
        String cartKey = getCartKey(userId);

        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = boundHashOperations.values();
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return new ArrayList<>();
        }

        //购物车列表顺序，根据添加时间进行倒序排序
        cartInfoList = cartInfoList.stream().sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime())).toList();

        //远程获取商品最新价格，给予页面价格变动提示
        List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).toList();
        R<List<SkuPrice>> skuPriceListResult = remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);
        if (skuPriceListResult.getCode() == R.FAIL) {
            throw new ServiceException(skuPriceListResult.getMsg());
        }

        List<SkuPrice> skuPriceList = skuPriceListResult.getData();  //最新价格
        //  List<SkuPrice> 转  Map<Long, BigDecimal>
        Map<Long, BigDecimal> skuIdToSalePriceMap = skuPriceList.stream().collect(Collectors.toMap(SkuPrice::getSkuId,SkuPrice::getSalePrice));

        //跟新商品最新价格，更新内存中商品最新价格，而不是购物车中价格。因为没有重新放回购物车中
        for (CartInfo cartInfo : cartInfoList) {
            cartInfo.setCartPrice(skuIdToSalePriceMap.get(cartInfo.getSkuId()));
        }

        return cartInfoList;
    }

    /**
     * 删除购物车商品
     * @param skuId
     */
    @Override
    public void deleteCart(Long skuId) {
        // 准备 redis hash 类型大key
        Long userId = SecurityContextHolder.getUserId();//从当前线程上获取共享数据，headerInterceptor 拦截器获取请求头信息，将数据绑定到当前线程上
        String cartKey = getCartKey(userId);
        //准备 redis hash 类型小key
        String hashkey = String.valueOf(skuId);

        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (boundHashOperations.hasKey(hashkey)) {
            boundHashOperations.delete(hashkey);
        }
    }

    /**
     * 修改选中状态
     * @param skuId
     * @param isChecked
     */
    @Override
    public void checkCart(Long skuId, Integer isChecked) {

        //准备redis hash 类型大key
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        // 准备 redis hash 类型小key
        String hashKey = String.valueOf(skuId);

        //获取redis缓存操作对象
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);

        if (boundHashOperations.hasKey(hashKey)) {
            CartInfo cartInfo = boundHashOperations.get(hashKey);
            cartInfo.setIsChecked(isChecked);
            boundHashOperations.put(hashKey,cartInfo);
        }
    }


    /**
     * 全选
     * @param isChecked
     */
    @Override
    public void allCheckCart(Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);

        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        //从Redis中获取当前用户的购物车信息列表
        List<CartInfo> cartInfoList = boundHashOperations.values();
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            for (CartInfo cartInfo : cartInfoList) {
                cartInfo.setIsChecked(isChecked);
                boundHashOperations.put(cartInfo.getSkuId().toString(),cartInfo);
            }
        }

    }

    @Override
    public void clearCart() {

        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);

        ////获取缓存对象
        if (redisTemplate.hasKey(cartKey)) {
            redisTemplate.delete(cartKey);
        }

    }

    @Override
    public List<CartInfo> getCartCheckedList(Long userId) {
        String cartKey = getCartKey(userId);
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = boundHashOperations.values();

        if (!CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList.stream().filter(cartInfo -> cartInfo.getIsChecked()==1).toList();
        }

        return new ArrayList<>();
    }

    /**
     *      filter是过滤操作，返回结果为true的数据；
     *      map的作用是将流中的每一个元素T映射为R。
     */
    @Override
    public Boolean updateCartPrice(Long userId) {
        //获取购物车所有商品。只更新下订单中商品在购物车中的价格
        String cartKey = getCartKey(userId); //获取 hash 大key
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey); //获取redis中的键
        List<CartInfo> cartInfoList = boundHashOperations.values(); // 获取购物车列表

        if (!CollectionUtils.isEmpty(cartInfoList)) { // 购物车不为空
            List<CartInfo> checkedCartInfoList = cartInfoList.stream().filter(cartInfo -> cartInfo.getIsChecked() == 1).toList();  //筛选购物车选中的商品
            if (!CollectionUtils.isEmpty(checkedCartInfoList)) { // 购物车选中的商品不为空
                List<Long> skuIdList = checkedCartInfoList.stream().map(cartInfo -> cartInfo.getSkuId()).toList();  // 将这些商品的Id 存到map中

                R<List<SkuPrice>> skuPriceListResult = remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER); // 远程调用 获取商品Id集合

                if (skuPriceListResult.getCode() == R.FAIL) { // 判定 500 远程调用状态
                    throw new ServiceException(skuPriceListResult.getMsg());  // 降级处理
                }

                List<SkuPrice> skuPricesList = skuPriceListResult.getData();  //获取商品最新价格

                Map<Long, BigDecimal> skuIdToSalePriceMap = skuPricesList.stream().collect(Collectors.toMap(SkuPrice::getSkuId, SkuPrice::getSalePrice)); //将商品id 售价 存到集合

                for (CartInfo checkedCartInfo : checkedCartInfoList) { //遍历购物车选中的商品
                    BigDecimal newPrice = skuIdToSalePriceMap.get(checkedCartInfo.getSkuId());  // 将最新价格 赋值给 newPrice
                    checkedCartInfo.setCartPrice(newPrice); //设置购物车价格为最新价格
                    checkedCartInfo.setSkuPrice(newPrice); //设置商品价格为最新价格
                    boundHashOperations.put(checkedCartInfo.getSkuId().toString(),checkedCartInfo); //更新缓存
                }
                return true;
            }
        }

        return false;
    }

    @Override
    public Boolean deleteCartCheckedList(Long userId) {
        String cartKey = getCartKey(userId);
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = boundHashOperations.values();

        if (!CollectionUtils.isEmpty(cartInfoList)) {
            for (CartInfo cartInfo : cartInfoList) {
                if (cartInfo.getIsChecked() == 1) {
                    boundHashOperations.delete(cartInfo.getSkuId().toString());
                }
            }
            return true;
        }

        return false;
    }
}